﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using AppToolkit.WPF.UI;
using log4net;

namespace AppToolkit.WPF.Services
{
    /// <summary>
    /// Visualizes exception using <see cref="ErrorReportDialog"/>.
    /// </summary>
    [Export(typeof(IErrorDisplay))]
    public class ErrorDisplayService : IErrorDisplay
    {
        private readonly ILog _logProvider;
        private readonly IErrorReportDialog _errorReportDialog;
        private Window _mainWindow;

        /// <summary>
        /// Default constructor
        /// </summary>
        public ErrorDisplayService()
        {
            _logProvider = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            _errorReportDialog = new ErrorReportDialog();
        }

        internal ErrorDisplayService(ILog logProvider, IErrorReportDialog errorReportDialog)
        {
            Contract.Requires<ArgumentNullException>(logProvider != null, "logProvider");
            Contract.Requires<ArgumentNullException>(errorReportDialog != null, "errorReportDialog");

            _logProvider = logProvider;
            _errorReportDialog = errorReportDialog;
        }

        /// <summary>
        /// Used to get the application MainWindow injected. If no export is defined, ErrorReportDialog will not have a parent.
        /// </summary>
        [Import(ContractDefinitions.MainWindow, AllowDefault = true)]
        public Window MainWindow
        {
            get { return _mainWindow; }
            set
            {
                if (_mainWindow != null)
                {
                    UnregisterEventSubscriptions(null,EventArgs.Empty);
                }
                
                _mainWindow = value;

                if (_mainWindow != null)
                {
                    _mainWindow.Dispatcher.UnhandledException += DispatcherOnUnhandledException;
                    _mainWindow.Closed += UnregisterEventSubscriptions;
                }
            }
        }

        /// <summary>
        /// <see cref="IErrorDisplay"/>
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="owner"></param>
        public void ShowException(Exception ex, Window owner = null)
        {
            _errorReportDialog.Owner = owner ?? this.MainWindow;
            _errorReportDialog.Exception = ex;

            _errorReportDialog.ShowDialog();
            
            if (!_errorReportDialog.IgnoreError)
            {
                _logProvider.Fatal("Unhandled exception caught which requires shutting down!", ex);
                throw ex;
            }
            _logProvider.Warn("Unhandled exception was ignored by user!", ex);
        }

        #region Internals
        private void UnregisterEventSubscriptions(object sender, EventArgs args)
        {
            _mainWindow.Dispatcher.UnhandledException -= DispatcherOnUnhandledException;
            _mainWindow.Closed -= UnregisterEventSubscriptions;
            _mainWindow = null;
        }

        private void DispatcherOnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs dispatcherUnhandledExceptionEventArgs)
        {
            ShowException(dispatcherUnhandledExceptionEventArgs.Exception);
        }
        #endregion
    }
}